import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.*;
import java.io.*;
import java.sql.*;
import java.util.zip.*;


public class SourceGenProto
{
	class CatAndPoints
	{
		String cat; int points;
		public CatAndPoints(String cat, int points)
		{
			this.cat = cat;
			this.points = points;
		}
	}
	
	class QIdAndPoints
	{
		int qId, points;
		public QIdAndPoints(int qId, int points)
		{
			this.qId = qId;
			this.points = points;
		}
	}
	/**
	 *	@param args
	 */
	public static void main(String[] args)
	{	
		int examID = Integer.parseInt(args[0]);
		String runCode = args[1];

		SourceGenProto sg = new SourceGenProto();
		try
		{
				DriverManager.registerDriver (new oracle.jdbc.driver.OracleDriver());
				Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@146.187.134.17:1542:oracle10g", "cscd494",	"R3tral435");
				//@machineName:port:SID, userid, password
			if(runCode.compareTo("generate")==0)
			{
				ArrayList<CatAndPoints> qCategory = new ArrayList<CatAndPoints>();
	
				qCategory = sg.getCategoryList(conn);
	
System.out.println("size cat: " + qCategory.size());
				//Each index in qID is an ArrayList of question_id, which correspond
				//to the index of qCategory for the name of that category
				ArrayList[] qID = new ArrayList[qCategory.size()];
				
				//initialize each array and grab the question_id
				for(int i=0;i<qCategory.size();i++)
				{
					qID[i] = new ArrayList<QIdAndPoints>();
					qID[i] = sg.getCategoryQuestion(conn, qCategory.get(i).cat);
System.out.println("size cat: " + qID[i].size());
				}

				sg.previewExam(conn, qCategory, qID);
			}
			else if(runCode.compareTo("package")==0)
			{
				sg.createExam(conn,examID);// Generate an exam question
							
				/*try
				{//Archive all file for exam
					ZipOutputStream zos = new ZipOutputStream(new FileOutputStream("ape.zip"));
					sg.createZip("Solution",zos);
					sg.createZip("Student",zos);
					zos.close();
				}
				catch(Exception e)
				{
					e.printStackTrace();
				}*/
			}
				//sg.uploadToDB(conn,"ape.zip");
				conn.close();//Close database connection
		}
		catch(SQLException sql)
		{
			sql.printStackTrace();
		}
	
	}

	/**
	 * Grab a list of all the question type that question should be generated for.
	 * @param conn A database connection
	 * @return An ArrayList of all current type(category) of question available
	 */
	public ArrayList<CatAndPoints> getCategoryList(Connection conn)
	{
		ArrayList<CatAndPoints> array = new ArrayList<CatAndPoints>();
		try
		{
			Statement quesType = conn.createStatement();
			ResultSet rset = quesType.executeQuery("SELECT type_code, points FROM question_type");

			while(rset.next())
			{
				array.add(new CatAndPoints(rset.getString("type_code"), rset.getInt("points")));
			}
			quesType.close();
			rset.close();
		}
		catch(SQLException sqlError)
		{
			sqlError.printStackTrace();
		}

		return array;
	}

	/**
	 * This will return list of question_id for a type of question passed in.
	 * @param conn	A database connection
	 * @param questionCode	The type(category) of question to grab from database
	 * @return An ArrayList of all question_id for the type(category) specified by questionCode
	 */
	public ArrayList<QIdAndPoints> getCategoryQuestion(Connection conn, String questionCode)
	{
		ArrayList<QIdAndPoints> store = new ArrayList<QIdAndPoints>();
		try
		{
			PreparedStatement quesType = conn.prepareStatement("Select question_id from question where question_type_code = ?");
			PreparedStatement quesPoint = conn.prepareStatement("SELECT points FROM QUESTION_CONTENT INNER JOIN QUESTION ON QUESTION.QUESTION_CONTENT_ID = QUESTION_CONTENT.QUESTION_CONTENT_ID WHERE QUESTION_ID=?");
			
			quesType.setString(1,questionCode);
			ResultSet r = quesType.executeQuery();

			while(r.next())
			{
				int qId = r.getInt("question_id");
				quesPoint.setInt(1, qId);
				ResultSet rPoint = quesPoint.executeQuery();
				
				rPoint.next();
				int point = rPoint.getInt("points");
				store.add(new QIdAndPoints(qId, point));
				
				rPoint.close();
			}
			r.close();//close the ResultSet
			quesType.close();//close Statement
			quesPoint.close();
		}
		catch(SQLException sqlError)
		{
			sqlError.printStackTrace();
		}

		return store;
	}

	/**
	 * Generate a list a potential question for an exam
	 * @param conn	A database connection
	 * @param qCategory	A list of question category
	 * @param qID	A list of question id
	 */
	public void previewExam(Connection conn, ArrayList<CatAndPoints> qCategory, ArrayList[] qID)
	{
		ArrayList<Integer> examQuestion = new ArrayList<Integer>();
		Random randGen = new Random();
		int randNum = 0;
		int curPoints, curQID;
		
		for(int i=0;i<qCategory.size();i++)
		{
			int remainPoints = qCategory.get(i).points;
			while(remainPoints > 0)
			{
				if(qID[i].size()>0)
				{
					randNum = randGen.nextInt(qID[i].size());
					curQID = ((QIdAndPoints)qID[i].get(randNum)).qId;
					curPoints = ((QIdAndPoints)qID[i].get(randNum)).points;
					
					if(remainPoints >= curPoints)
					{
						examQuestion.add(curQID);
						System.out.println(curQID);
						remainPoints -= curPoints;
					}
				}
			}
		}
		try
		{
			PreparedStatement uploadStmt = conn.prepareStatement("INSERT INTO EXAM_QUESTION VALUES(?,?,0)");
			uploadStmt.setInt(1,4);
			for(int i=0;i<examQuestion.size();i++)
			{
				uploadStmt.setInt(2,(Integer)examQuestion.get(i));
				uploadStmt.execute();
			}
			uploadStmt.close();
		}
		catch(SQLException sqlError)
		{
			sqlError.printStackTrace();
		}
	}

	/**
	 * Grab all the file need for each questionID from the list
	 * @param conn A connection to the database
	 * @param questionID An ArrayList of question_id on this exam
	 */
	public void createExam(Connection conn, int examID)//, ArrayList<Integer> questionID)
	{
		ArrayList<Integer> examQuestion = new ArrayList<Integer>();
		
		/*int[] array={1,1};
		for(int i=0;i<1;i++)
		{
			String directory = "Part"+(i+1);
			new File("Student/" + directory).mkdirs();
			new File("Solution/" + directory).mkdirs();
			generateQuestion(conn,array[i], directory);
		}*/
		try
		{
			PreparedStatement pstmt = conn.prepareStatement("SELECT question_id FROM EXAM_QUESTION WHERE exam_id=?");
			pstmt.setInt(1,examID);
			ResultSet rset = pstmt.executeQuery();
			
			while(rset.next())
			{
				examQuestion.add(rset.getInt("question_id"));
			}
			
			for(int i=0;i<examQuestion.size();i++)
			{
				String directory = "Part"+(i+1);
				new File("Student/" + directory).mkdirs();
				new File("Solution/" + directory).mkdirs();
				generateQuestion(conn,examQuestion.get(i), directory);
			}
		}
		catch(SQLException sqlError)
		{
			sqlError.printStackTrace();
		}
	}

	/**
	 * Grab all the file need to generate a question. Eventually it will accept a question_id to generate more different question.
	 * @param conn A database connection
	 * @param qID	ID of the question
	 * @param pDirectory	The directory name where the file belong
	 */
	public void generateQuestion(Connection conn, int qID, String pDirectory)
	{
		try
		{
			// Grab Provided Output
			PreparedStatement qOutput = conn.prepareStatement("SELECT test_input, input_file_name, provided_input, provided_output FROM QUESTION_CONTENT INNER JOIN QUESTION ON QUESTION.QUESTION_CONTENT_ID = QUESTION_CONTENT.QUESTION_CONTENT_ID WHERE QUESTION_ID=?");
			qOutput.setInt(1,qID);
			ResultSet qResult = qOutput.executeQuery();
			
			qResult.next();
			Blob outputBlob = qResult.getBlob("provided_output");
			//System.out.println(outputBlob.length());
			if(outputBlob != null)
			{
				BufferedOutputStream sStream = new BufferedOutputStream(new FileOutputStream(new File("Student/" + pDirectory, "sampleOutput.txt")));
				sStream.write(outputBlob.getBytes(1, (int)outputBlob.length()), 0, (int)outputBlob.length());//Write out the length of blob to file
				sStream.flush();
				sStream.close();
			}
			
			//Grab student test input
			String fname = null;
			fname = qResult.getString("input_file_name");
			Blob inputBlob = qResult.getBlob("provided_input");
			
			if(inputBlob != null)
			{
				BufferedOutputStream stuStream = new BufferedOutputStream(new FileOutputStream(new File("Student/" + pDirectory, fname)));
				stuStream.write(inputBlob.getBytes(1, (int)inputBlob.length()), 0, (int)inputBlob.length());
				
				stuStream.flush();
				stuStream.close();
			}
			
			//Grab input used to score exam
			fname = qResult.getString("test_input");
			Blob testInputBlob = qResult.getBlob("test_input");
			
			if(testInputBlob != null)
			{
				BufferedOutputStream solStream = new BufferedOutputStream(new FileOutputStream(new File("Solution/" + pDirectory, fname)));
				solStream.write(testInputBlob.getBytes(1, (int)testInputBlob.length()), 0, (int)testInputBlob.length());
				
				solStream.flush();
				solStream.close();
			}
			qResult.close();
			qOutput.close();
			
			
			//Grab Source File
//			System.out.println(qID);
			PreparedStatement prepStatement = conn.prepareStatement("SELECT SOURCE_FILE.source_file_id AS \"File ID\", SOURCE_FILE.source_file_name AS \"File Name\",SOURCE_FILE.source_file AS \"Source\" FROM SOURCE_FILE INNER JOIN QUESTION ON QUESTION.question_id = SOURCE_FILE.question_id WHERE QUESTION.question_id = ?");
			prepStatement.setInt(1,qID);//Change question_id
			
			ResultSet rset = prepStatement.executeQuery(); //run PreparedStatement

			while	(rset.next())//While the result is not empty
			{		
				String filename =	rset.getString("File Name");
				
				if(filename.toLowerCase().contains(".java"))//Check if the file is .Java
				{
					String curLine	= null;
					int index = 0;
					Scanner scan =	new Scanner(rset.getBinaryStream("Source"));

					PrintWriter	studentFile	= new	PrintWriter(new File("Student/"+pDirectory,filename));
					PrintWriter	solutionFile =	new PrintWriter(new File("Solution/"+pDirectory,filename));
					while(scan.hasNext())
					{
						curLine	= scan.nextLine();
						if((index = curLine.indexOf("/*ans bl*/")) >0)
						{
							int index2 = curLine.indexOf("/*ans ab*/")+10;
							String temp = curLine.substring(0,index);
							String temp1 = curLine.substring(index2,curLine.length());
							studentFile.printf(temp);
							studentFile.println(temp1);
							solutionFile.println(curLine);
							curLine = scan.nextLine();
						}
						if(curLine.equals("/*ans bl*/"))
						{
							curLine = scan.nextLine();
							while(!curLine.equals("/*ans ab*/"))
							{
								solutionFile.println(curLine);
								curLine = scan.nextLine();
							}
						}
						else
						{
							studentFile.println(curLine);
							solutionFile.println(curLine);
						}
					}
					studentFile.close();
					solutionFile.close();
				}
				else// if(filename.indexOf(".java")<0)//Not a java file don't need to parse
				{
					Blob blob = rset.getBlob("Source");
					BufferedOutputStream studentStream = new BufferedOutputStream(new FileOutputStream(new File("Student/" + pDirectory, filename)));
					BufferedOutputStream solutionStream = new BufferedOutputStream(new FileOutputStream(new File("Solution/" + pDirectory, filename)));

					studentStream.write(blob.getBytes(1, (int)blob.length()), 0, (int)blob.length());//Write out the length of blob to file
					solutionStream.write(blob.getBytes(1, (int)blob.length()), 0, (int)blob.length());

					studentStream.flush();
					solutionStream.flush();

					studentStream.close();
					solutionStream.close();
				}
			}
			rset.close();//Close ResultSet
			prepStatement.close();//Close PreparedStatement
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}

	/**
	 * Create a zip file with the content of the directory passed in.
	 * @param dir	The directory to be zip
	 * @param zos	A ZipOutputStream where files will be written
	 * http://www.devx.com/tips/Tip/14049
	 */
	public void createZip(String dir, ZipOutputStream zos)
	{
		try
		{
			File zipDir = new File(dir);
			String[] dirList = zipDir.list();
			byte[] buffer = new byte[1000];
			int bytesIn = 0;
			
			for(int i=0;i<dirList.length;i++)
			{
				File f = new File(zipDir,dirList[i]);
				if(f.isDirectory())
				{
					String filePath = f.getPath();
					createZip(filePath, zos);
					
					continue;
				}
				
				FileInputStream fis = new FileInputStream(f);
				ZipEntry entry = new ZipEntry(f.getPath());
				zos.putNextEntry(entry);
				while((bytesIn = fis.read(buffer)) != -1)
				{
					zos.write(buffer, 0, bytesIn);
				}
				fis.close();
			}
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}

	}
	
	/**
	 * Upload an archive with all data related to an exam.
	 * @param conn	A database connection
	 * @param filename	Name of file to upload
	 */
	public void uploadToDB(Connection conn, String filename)
	{
		try
		{
			PreparedStatement uploadStmt = conn.prepareStatement("Insert INTO BTAB FILE_NAME VALUES(?,?,?)");
			
			File blob = new File(filename);
			FileInputStream is = new FileInputStream(blob);
			uploadStmt.setInt(1, 1);//Set the ID
			uploadStmt.setBinaryStream(2, is, (int)blob.length());//set file upload stream
			uploadStmt.setString(3,filename);//set the filename
			uploadStmt.execute();
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
}
